use dodgy_2d::Vec2;
use rstar::{RTreeObject, AABB};
use serde::{Deserialize, Serialize};

use crate::{mash_data::Edge, F64Point};

#[cfg(feature = "napi")]
use napi_derive::napi;

#[derive(Clone)]
pub struct DodgyAgent(pub dodgy_2d::Agent);


impl DodgyAgent {
    pub fn get_aabb(&self) -> AABB<F64Point> {
        let half_radius = self.0.radius/2.0;
        let min_x = self.0.position.x - half_radius;
        let max_x = self.0.position.x + half_radius;
        let min_y = self.0.position.y - half_radius;
        let max_y = self.0.position.y + half_radius;
        AABB::from_corners(F64Point::new(min_x as f64, min_y as f64), F64Point::new(max_x as f64, max_y as f64))
    }
}

#[derive(Clone)]
pub struct DodgyObstacle {
    pub obstacle:dodgy_2d::Obstacle,
    aabb:AABB<F64Point>,
}

impl DodgyObstacle {
    pub fn new(polygon: Vec<Vec2>)->Self{
        let mut min_point = F64Point::new(f64::MAX, f64::MAX);
        let mut max_point = F64Point::new(f64::MIN, f64::MIN);

        for vertex in &polygon {
            min_point.x = min_point.x.min(vertex.x as f64);
            min_point.y = min_point.y.min(vertex.y as f64);
            max_point.x = max_point.x.max(vertex.x as f64);
            max_point.y = max_point.y.max(vertex.y as f64);
        }

        let obstacle = dodgy_2d::Obstacle::Closed { vertices: polygon };
        Self {
            obstacle,
            aabb: AABB::from_corners(min_point, max_point)
        }
    }

}

/// 判断一个点是否位于不可行走区域
pub fn is_point_in_obstacle(point: &Vec2, obstacle: &dodgy_2d::Obstacle) -> bool {
    match obstacle {
        dodgy_2d::Obstacle::Closed { vertices } => {
            let inside = is_point_in_polygon(point, vertices);
            let area = polygon_signed_area(vertices);
            if area > 0.0 {
                // Counter-clockwise polygon, interior is unwalkable
                inside
            } else {
                // Clockwise polygon, exterior is unwalkable
                !inside
            }
        }
        dodgy_2d::Obstacle::Open { vertices } => {
            for i in 0..(vertices.len() - 1) {
                let a = &vertices[i];
                let b = &vertices[i + 1];
                if is_point_left_of_edge(point, a, b) {
                    return true;
                }
            }
            false
        }
    }
}

/// 计算多边形的有向面积，以确定其顺时针或逆时针方向
fn polygon_signed_area(vertices: &[Vec2]) -> f32 {
    let n = vertices.len();
    let mut area = 0.0;
    for i in 0..n {
        let vi = &vertices[i];
        let vj = &vertices[(i + 1) % n];
        area += vi.x * vj.y - vj.x * vi.y;
    }
    area * 0.5
}

/// 判断点是否在多边形内部
fn is_point_in_polygon(point: &Vec2, vertices: &[Vec2]) -> bool {
    let mut crossings = 0;
    let n = vertices.len();
    let (x, y) = (point.x, point.y);

    for i in 0..n {
        let a = &vertices[i];
        let b = &vertices[(i + 1) % n];

        if (a.y > y) != (b.y > y) {
            let slope = (b.x - a.x) / (b.y - a.y);
            let intersect_x = a.x + slope * (y - a.y);
            if x < intersect_x {
                crossings += 1;
            }
        }
    }

    crossings % 2 == 1
}

/// 判断点是否在边的左侧
fn is_point_left_of_edge(point: &Vec2, a: &Vec2, b: &Vec2) -> bool {
    let cross = (b.x - a.x) * (point.y - a.y) - (b.y - a.y) * (point.x - a.x);
    cross >= 0.0
}

impl DodgyObstacle {
    pub  fn get_aabb(&self) -> AABB<F64Point> {
        self.aabb.clone()
    }

    /// 检查一个点是否在闭合障碍物内
    pub fn is_point_in_closed_obstacle(&self, point: Vec2) -> bool {
        is_point_in_obstacle(&point, &self.obstacle)
    }

}


#[derive(Clone, Copy, Serialize, Deserialize)]
pub enum SimulatorMarginType {
    AgentRadius,
    Distance,
}

#[derive(Clone, Copy, Serialize, Deserialize)]
pub struct SimulatorMargin{
    pub simulator_margint_type:SimulatorMarginType,
    pub distance:Option<f64>
}

#[cfg_attr(feature = "napi", napi(object))]
pub struct NapiSimulatorMargin {
    pub simulator_margint_type:String,
    pub distance:Option<f64>
}

impl Into<SimulatorMargin> for NapiSimulatorMargin {
    fn into(self) -> SimulatorMargin {
        let simulator_margint_type = match self.simulator_margint_type.to_uppercase().as_str() {
            "AGENTRADIUS" => SimulatorMarginType::AgentRadius,
            "DISTANCE" => SimulatorMarginType::Distance,
            _ => SimulatorMarginType::AgentRadius
        };
        SimulatorMargin {
            simulator_margint_type,
            distance:self.distance
        }
    }
}

#[cfg_attr(feature = "napi", napi(object))]
pub struct NapiAgentParameters {
    pub max_speed: f64,
    pub obstacle_margin: NapiSimulatorMargin,
    pub time_horizon: f64,
    pub obstacle_time_horizon: f64,
    pub check_agent_obstract: bool,
    pub has_obstract:bool,
    pub can_move:bool,
    pub target_point:Option<F64Point>,
    pub min_check_stop_speed:f64,
    pub search_path_skip_distance:f64,
    pub entity_id:Option<String>,
    pub camp:Option<f64>,
    pub team_id:Option<f64>,
    pub entity_type_mask:Option<i32>
}

impl Into<AgentParameters> for NapiAgentParameters {
    fn into(self) -> AgentParameters {
        AgentParameters {
            max_speed: self.max_speed as f32,
            obstacle_margin: self.obstacle_margin.into(),
            time_horizon: self.time_horizon as f32,
            obstacle_time_horizon: self.obstacle_time_horizon as f32,
            check_agent_obstract: self.check_agent_obstract,
            has_obstract:self.has_obstract,
            can_move:self.can_move,
            target_point:self.target_point,
            min_check_stop_speed:self.min_check_stop_speed as f32,
            search_path_skip_distance:self.search_path_skip_distance,
            entity_id:self.entity_id,
            camp:self.camp,
            team_id:self.team_id,
            entity_type_mask:self.entity_type_mask
        }
    }
}

#[derive(Serialize, Deserialize)]
pub struct AgentParameters {
    pub max_speed: f32,
    pub obstacle_margin: SimulatorMargin,
    pub time_horizon: f32,
    pub obstacle_time_horizon: f32,
    pub check_agent_obstract: bool,
    pub has_obstract:bool,
    pub can_move:bool,
    pub target_point:Option<F64Point>,
    pub min_check_stop_speed:f32,
    pub search_path_skip_distance:f64,
    pub entity_id:Option<String>,
    pub camp:Option<f64>,
    pub team_id:Option<f64>,
    pub entity_type_mask:Option<i32>
}

#[cfg_attr(feature = "napi", napi(object))]
pub struct NapiAgent {
    pub position:F64Point,
    pub velocity:F64Point,
    pub radius:f64,
    pub avoidance_responsibility:f64,
}

impl Into<Agent> for NapiAgent {
    fn into(self) -> Agent {
        Agent {
            position:self.position,
            velocity:self.velocity,
            radius:self.radius as f32,
            avoidance_responsibility:self.avoidance_responsibility as f32
        }
    }
}

#[derive(Serialize, Deserialize)]
pub struct Agent {
    pub position:F64Point,
    pub velocity:F64Point,
    pub radius:f32,
    pub avoidance_responsibility:f32
}

impl Into<DodgyAgent> for Agent {
    fn into(self) -> DodgyAgent {
        DodgyAgent(dodgy_2d::Agent {
            position: self.position.into(),
            velocity: self.velocity.into(),
            radius:self.radius,
            avoidance_responsibility:self.avoidance_responsibility
        })
    }
}

#[derive(Serialize, Deserialize)]
#[cfg_attr(feature = "napi", napi(object))]
pub struct Obstacle {
    pub polygon:Vec<F64Point>,
    pub check_direction:bool,
}

fn is_clockwise(polygon: &Vec<F64Point>) -> bool {
    let mut sum = 0.0;
    let n = polygon.len();
    
    for i in 0..n {
        let current = &polygon[i];
        let next = &polygon[(i + 1) % n]; // 下一点，首尾相连
        sum += (next.x - current.x) * (next.y + current.y);
    }
    
    // 如果总和为正则为顺时针
    sum > 0.0
}

impl Into<DodgyObstacle> for Obstacle {
    fn into(self) -> DodgyObstacle {
        let mut polygon = self.polygon;
        if self.check_direction {
            if is_clockwise(&polygon) {
                polygon.reverse();
            }
        }
        let vertices = polygon.iter().map(|item| item.into()).collect();
        DodgyObstacle::new(vertices)
    }
}

pub struct AgentAABBObject {
    pub agent: i32,
    pub aabb: AABB<F64Point>,
    pub entity_id:Option<String>,
    pub camp:Option<f64>,
    pub team_id:Option<f64>,
    pub entity_type_mask:Option<i32>
}

impl PartialEq for AgentAABBObject {
    fn eq(&self, other: &Self) -> bool {
        self.agent == other.agent
    }
}

impl Eq for AgentAABBObject {}

impl RTreeObject for AgentAABBObject {
    type Envelope = AABB<F64Point>;

    fn envelope(&self) -> Self::Envelope {
        self.aabb
    }
}

pub struct ObstacleAABBObject {
    pub obstacle: i32,
    pub aabb: AABB<F64Point>,
}

impl PartialEq for ObstacleAABBObject {
    fn eq(&self, other: &Self) -> bool {
        self.obstacle == other.obstacle
    }
}

impl Eq for ObstacleAABBObject {}

impl RTreeObject for ObstacleAABBObject {
    type Envelope = AABB<F64Point>;

    fn envelope(&self) -> Self::Envelope {
        self.aabb
    }
}

pub struct EdgeAABBObject {
    pub edge: Edge,
    obstacle: dodgy_2d::Obstacle,
}

impl EdgeAABBObject {
    pub fn with_edge(edge: &Edge) -> Self {
        let obstacle = dodgy_2d::Obstacle::Open {
            vertices: vec![Vec2::new(edge.0.x as f32, edge.0.y as f32), Vec2::new(edge.1.x as f32, edge.1.y as f32)],
        };
        Self { edge: *edge, obstacle }
    }

    pub fn get_obstacle(&self)->&dodgy_2d::Obstacle{
        &self.obstacle
    }
}

impl PartialEq for EdgeAABBObject {
    fn eq(&self, other: &Self) -> bool {
        self.edge == other.edge
    }
}

impl Eq for EdgeAABBObject {}

impl RTreeObject for EdgeAABBObject {
    type Envelope = AABB<F64Point>;

    fn envelope(&self) -> Self::Envelope {
        AABB::from_corners(self.edge.0, self.edge.1)
    }
}
